home *** CD-ROM | disk | FTP | other *** search
/ Programming Sound Cards / Programming Sound Cards.iso / sound_87 / modloade.pas < prev    next >
Pascal/Delphi Source File  |  1995-01-01  |  13KB  |  428 lines

  1. UNIT ModLoader;
  2.  
  3. INTERFACE
  4.  
  5. USES Objects, SongUnit;
  6.  
  7.  
  8.  
  9.  
  10. PROCEDURE LoadModFileFormat  (VAR Song: TSong; VAR St: TStream; VAR Header: TSongHeader);
  11.  
  12.  
  13.  
  14.  
  15. IMPLEMENTATION
  16.  
  17. USES SongElements, Heaps, AsciiZ;
  18.  
  19.  
  20.  
  21.  
  22. {----------------------------------------------------------------------------}
  23. { Internal definitions. Format of the files.                                 }
  24. {____________________________________________________________________________}
  25.  
  26. TYPE
  27.   TModFileMagic = ARRAY[0..3] OF CHAR;
  28.  
  29. CONST
  30.   Mod31MagicM_K_ : TModFileMagic = ( 'M', '.', 'K', '.' );
  31.   Mod31MagicFLT4 : TModFileMagic = ( 'F', 'L', 'T', '4' );
  32.   Mod31Magic6CHN : TModFileMagic = ( '6', 'C', 'H', 'N' );
  33.   Mod31Magic8CHN : TModFileMagic = ( '8', 'C', 'H', 'N' );
  34.  
  35. TYPE
  36.  
  37.   { Instrument in a MOD file. 30 bytes. }
  38.  
  39.   TModFileInstrument = RECORD
  40.     Name       : ARRAY [1..22] OF CHAR; { AsciiZ string, name of the instrument. }
  41.     Len        : WORD;                  { Length of the sample DIV 2.            }
  42.     FineTune,                           { Fine tuning value.                     }
  43.     Vol        : BYTE;                  { Default volume.                        }
  44.     LoopStart,                          { Offset of the loop DIV 2.              }
  45.     LoopLen    : WORD;                  { Length of the loop DIV 2.              }
  46.   END;
  47.  
  48.   { Note in the file. 4 bytes. }
  49.  
  50.   PModFileNote = ^TModFileNote;
  51.   TModFileNote = RECORD
  52.     CASE INTEGER OF
  53.       1: (l              : LONGINT);
  54.       2: (w1, w2         : WORD);
  55.       3: (b1, b2, b3, b4 : BYTE);
  56.   END;
  57.  
  58.   PModFilePattern = ^TModFilePattern;
  59.   TModFilePattern =
  60.     RECORD
  61.       CASE BYTE OF
  62.         4 : ( Patt4 : ARRAY [0..63] OF ARRAY [1..4] OF TModFileNote );
  63.         5 : ( Patt5 : ARRAY [0..63] OF ARRAY [1..5] OF TModFileNote );
  64.         6 : ( Patt6 : ARRAY [0..63] OF ARRAY [1..6] OF TModFileNote );
  65.         7 : ( Patt7 : ARRAY [0..63] OF ARRAY [1..7] OF TModFileNote );
  66.         8 : ( Patt8 : ARRAY [0..63] OF ARRAY [1..8] OF TModFileNote );
  67.     END;                                                   
  68.  
  69.   { 15 samples module header format. 600 bytes. }
  70.  
  71.   PModFile15 = ^TModFile15;
  72.   TModFile15 = RECORD
  73.     Name        : ARRAY [1..20] OF CHAR;               { AsciiZ song name.                   }
  74.     Samples     : ARRAY [1..15] OF TModFileInstrument; { Instruments.                        }
  75.     SongLen     : BYTE;                                { Length of the sequency of the song. }
  76.     SongRep     : BYTE;                                { Song loop start position.           }
  77.     PatternList : ARRAY [0..127] OF BYTE;              { Pattern sequencies.                 }
  78.   END;
  79.  
  80.   { 31 samples module header format. 1084 bytes. }
  81.  
  82.   PModFile31 = ^TModFile31;
  83.   TModFile31 = RECORD
  84.     Name        : ARRAY [1..20] OF CHAR;               { AsciiZ song name.                   }
  85.     Samples     : ARRAY [1..31] OF TModFileInstrument; { Instruments.                        }
  86.     SongLen     : BYTE;                                { Length of the sequency of the song. }
  87.     SongRep     : BYTE;                                { Song loop start position.           }
  88.     PatternList : ARRAY [0..127] OF BYTE;              { Pattern sequencies.                 }
  89.     Magic       : TModFileMagic;                       { Magic number ("M.K.", "FLT4", etc.) }
  90.   END;
  91.  
  92.  
  93.  
  94.  
  95.  
  96. PROCEDURE ProcessPatterns(VAR Song: TSong; VAR St: TStream; Num: WORD);
  97.   VAR
  98.     Patt      : TModFilePattern;
  99.     FullTrack : TFullTrack;
  100.     Pattern   : PPattern;
  101.     Track     : PTrack;
  102.     i, j      : WORD;
  103.     n, t      : WORD;
  104.     l         : LONGINT;
  105.   BEGIN
  106.     t := 1;
  107.     FOR n := 1 TO Num DO
  108.       BEGIN
  109.         Pattern := Song.GetPattern(n);
  110.         IF Pattern = NIL THEN
  111.           BEGIN
  112.             Song.Status := msOutOfMemory;
  113.             EXIT;
  114.           END;
  115.  
  116.         WITH Pattern^.Patt^ DO
  117.           BEGIN
  118.             NNotes   := 64;
  119.             NChans   := Song.NumChannels;
  120.             Tempo    := 0;
  121.             BPM      := 0;
  122.           END;
  123.  
  124.         l := St.GetPos;
  125.         St.Read(Patt, 64*4*Song.NumChannels);
  126.  
  127.         IF St.Status <> stOk THEN
  128.           BEGIN
  129.             Song.Status := msFileTooShort;
  130.             EXIT;
  131.           END;
  132.  
  133.         CASE Song.NumChannels OF
  134.           4 : FOR i := 63 DOWNTO 0 DO
  135.                 FOR j := Song.NumChannels DOWNTO 1 DO
  136.                   Patt.Patt8[i][j] := Patt.Patt4[i][j];
  137.           5 : FOR i := 63 DOWNTO 0 DO
  138.                 FOR j := Song.NumChannels DOWNTO 1 DO
  139.                   Patt.Patt8[i][j] := Patt.Patt5[i][j];
  140.           6 : FOR i := 63 DOWNTO 0 DO
  141.                 FOR j := Song.NumChannels DOWNTO 1 DO
  142.                   Patt.Patt8[i][j] := Patt.Patt6[i][j];
  143.           7 : FOR i := 63 DOWNTO 0 DO
  144.                 FOR j := Song.NumChannels DOWNTO 1 DO
  145.                   Patt.Patt8[i][j] := Patt.Patt7[i][j];
  146.         END;
  147.  
  148.         FOR j := 1 TO Song.NumChannels DO
  149.           BEGIN
  150.             FillChar(FullTrack, SizeOf(FullTrack), 0);
  151.  
  152.             FOR i := 0 TO 63 DO
  153.               WITH FullTrack[i], Patt.Patt8[i][j] DO
  154.                 BEGIN
  155.                   Command     := TModCommand((b3 AND $F) + 1);
  156.                   IF Command = mcExtended THEN
  157.                     BEGIN
  158.                       Parameter := b4 AND $F;
  159.                       Command   := TModCommand(($11 + (b4 SHR 4)));
  160.                     END
  161.                   ELSE IF (Command = mcArpeggio) AND (b4 = 0) THEN
  162.                     BEGIN
  163.                       Parameter := 0;
  164.                       Command   := mcNone;
  165.                     END
  166.                   ELSE
  167.                     Parameter := b4;
  168.  
  169.                   Period     := b2 + (WORD(b1 AND $7) SHL 8);
  170.                   Instrument := (b3 SHR 4) + (b1 AND 16);
  171.  
  172.                   IF ((Command = mcEndPattern) OR (Command = mcJumpPattern)) AND
  173.                      (Pattern^.Patt^.NNotes > i + 1) THEN
  174.                     Pattern^.Patt^.NNotes := i + 1;
  175.  
  176.                   IF (Command = mcSetVolume) AND (Parameter > $40) THEN
  177.                     Parameter := $40;
  178.  
  179.                   IF (Command = mcJumpPattern) THEN
  180.                     Parameter := (Parameter AND $0F) +
  181.                                  (Parameter SHR   4)*10 + 1;
  182.  
  183.                   IF (Command = mcEndPattern) THEN
  184.                     Parameter := (Parameter AND 63) + 1;
  185.                 END;
  186.  
  187.             Track := Song.GetTrack(t);
  188.             IF Track = NIL THEN
  189.               BEGIN
  190.                 Song.Status := msOutOfMemory;
  191.                 EXIT;
  192.               END;
  193.  
  194.             Track^.SetFullTrack(FullTrack);
  195.  
  196.             Pattern^.Patt^.Channels[j] := t;
  197.  
  198.             INC(t);
  199.           END;
  200.  
  201.       END;
  202.   END;
  203.  
  204.  
  205. PROCEDURE ProcessInstruments(VAR Song: TSong; VAR St: TStream; Mod31: TModFile31);
  206.   VAR
  207.     Instrument : TInstrumentRec;
  208.     Instr      : PInstrument;
  209.     i          : WORD;
  210.   BEGIN
  211.     FOR i := 1 TO 31 DO
  212.       WITH Instrument DO
  213.         BEGIN
  214.           FillChar(Instrument, SizeOf(Instrument), 0);
  215.  
  216.           Instr := Song.GetInstrument(i);
  217.           IF Instr = NIL THEN
  218.             BEGIN
  219.               Song.Status := msOutOfMemory;
  220.               EXIT;
  221.             END;
  222.  
  223.           Instr^.SetName(StrASCIIZ(Mod31.Samples[i].Name, 22));
  224.  
  225.           Len  := LONGINT(SWAP(Mod31.Samples[i].Len)      ) SHL 1;
  226.  
  227.           IF Len > St.GetSize - St.GetPos THEN
  228.             BEGIN
  229.               Len := St.GetSize - St.GetPos;
  230.               Song.Status := msFileTooShort;
  231.             END;
  232.  
  233.           IF Len > 0 THEN
  234.             BEGIN
  235.  
  236.               Reps := LONGINT(SWAP(Mod31.Samples[i].LoopStart)) SHL 1;
  237.               Repl := LONGINT(SWAP(Mod31.Samples[i].LoopLen)  ) SHL 1;
  238.               Vol  :=              Mod31.Samples[i].Vol;
  239.  
  240.               IF Repl        > Len THEN Repl := Len;
  241.               IF Reps + Repl > Len THEN Reps := Len - Repl;
  242.  
  243.               IF Mod31.Samples[i].Vol > $40 THEN
  244.                 Mod31.Samples[i].Vol := $40;
  245.  
  246.               IF Vol > $40 THEN
  247.                 Vol := $40;
  248.  
  249.               IF Len <= MaxSample THEN
  250.                 BEGIN
  251.                   FullHeap.HGetMem(POINTER(Data), Len);
  252.                   IF Data = NIL THEN BEGIN
  253.                     Song.Status := msOutOfMemory;
  254.                     EXIT;
  255.                   END;
  256.  
  257.                   St.Read(Data^, Len);
  258.  
  259.                   IF St.Status <> stOk THEN BEGIN
  260.                     Song.Status := msFileDamaged;
  261.                     EXIT;
  262.                   END;
  263.  
  264. {
  265.                   FOR w := 0 TO Len - 1 DO
  266.                     IF Instruments[i].data^[w] = -128 THEN
  267.                       Instruments[i].data^[w] := -127;
  268. }
  269.                 END
  270.               ELSE
  271.                 BEGIN
  272.                   FullHeap.HGetMem(POINTER(Data), MaxSample);
  273.                   FullHeap.HGetMem(POINTER(Xtra), Len-MaxSample);
  274.  
  275.                   IF (Data = NIL) OR (Xtra = NIL) THEN BEGIN
  276.                     Song.Status := msOutOfMemory;
  277.                     EXIT;
  278.                   END;
  279.  
  280.                   St.Read(Data^, MaxSample);
  281.                   St.Read(Xtra^, Len-MaxSample);
  282.  
  283.                   IF St.Status <> 0 THEN BEGIN
  284.                     Song.Status := msFileDamaged;
  285.                     EXIT;
  286.                   END;
  287.                 END;
  288.  
  289.               Instr^.Change (@Instrument);
  290.             END;
  291.         END;
  292.   END;
  293.  
  294.  
  295. PROCEDURE LoadMod(VAR Song: TSong; VAR St: TStream; VAR Mod31: TModFile31);
  296.   VAR
  297.     j, k,
  298.     i, w       : WORD;
  299.     IsMod31    : BOOLEAN;
  300.     NumberOfPatterns : WORD;
  301.  
  302.   BEGIN
  303.  
  304.     { Initial checkings to see if it's a real MOD. }
  305.  
  306.     Song.Status := msFileDamaged;
  307.  
  308.     FOR i := 0 TO 127 DO
  309.       IF Mod31.PatternList[i] > 63 THEN EXIT;
  310.  
  311.     FOR i := 1 TO 20 DO
  312.       IF (Mod31.Name[i] < ' ') AND
  313.          (Mod31.Name[i] <> #0) THEN EXIT;
  314.  
  315.     FOR j := 1 TO 31 DO
  316.       FOR i := 1 TO 22 DO
  317.         IF (Mod31.Samples[j].Name[i] < ' ') AND
  318.            (Mod31.Samples[j].Name[i] <> #0) THEN EXIT;
  319.  
  320.     IF (Mod31.SongLen > 128) OR (Mod31.SongRep > 128) THEN EXIT;
  321.  
  322.  
  323.     { Processing of the header }
  324.  
  325.     Song.Status := msOK;
  326.  
  327.     Song.Name := FullHeap.HNewStr(StrASCIIZ(Mod31.Name, 20));
  328.  
  329.     Song.InitialTempo := 6;
  330.     Song.InitialBPM   := 125;
  331.     Song.Volume       := 255;
  332.  
  333.     FOR i := 0 TO 127 DO
  334.       INC(Mod31.PatternList[i]);
  335.     Move(Mod31.PatternList, Song.PatternSequence^, Mod31.SongLen);
  336.     Song.SequenceLength   := Mod31.SongLen;
  337.     Song.SequenceRepStart := Mod31.SongRep + 1;
  338.  
  339.     NumberOfPatterns := 0;
  340.     FOR i := 0 TO 127 DO
  341.       IF NumberOfPatterns < Mod31.PatternList[i] THEN
  342.         NumberOfPatterns := Mod31.PatternList[i];
  343.  
  344.  
  345.     { Processing of the patterns (the partiture) }
  346.  
  347.     ProcessPatterns(Song, St, NumberOfPatterns);
  348.     IF Song.Status > msOk THEN EXIT;
  349.  
  350.  
  351.     { Processing of the instruments }
  352.  
  353.     ProcessInstruments(Song, St, Mod31);
  354.     IF Song.Status > msFileTooShort THEN EXIT;
  355.   END;
  356.  
  357.  
  358.  
  359.  
  360. PROCEDURE LoadMod15(VAR Song: TSong; VAR St: TStream; VAR Header: TSongHeader);
  361.   VAR
  362.     i     : WORD;
  363.     Mod31 : TModFile31 ABSOLUTE Header;
  364.     Mod15 : TModFile15 ABSOLUTE Header;
  365.   BEGIN
  366.     Move(Mod15.SongLen, Mod31.SongLen, 130);
  367.     FOR i := 16 TO 31 DO
  368.       FillChar(Mod31.Samples[i], SizeOf(Mod31.Samples[i]), 0);
  369.  
  370.     St.Seek(St.GetPos - SizeOf(TModFile31) + SizeOf(TModFile15));
  371.  
  372.     LoadMod(Song, St, Mod31);
  373.   END;
  374.  
  375.  
  376. PROCEDURE LoadModFileFormat  (VAR Song: TSong; VAR St: TStream; VAR Header: TSongHeader);
  377.   VAR
  378.     Mod31 : TModFile31 ABSOLUTE Header;
  379.   BEGIN
  380.     St.Seek(St.GetPos + SizeOf(TModFile31));
  381.  
  382.     IF (Mod31.Magic = Mod31MagicM_K_) THEN
  383.       BEGIN
  384.         IF Song.FileExt = '.WOW' THEN
  385.           BEGIN
  386.             Song.NumChannels := 8;
  387.             Song.FileFormat  := mffWow8;
  388.           END
  389.         ELSE
  390.           BEGIN
  391.             Song.NumChannels := 4;
  392.             Song.FileFormat  := mffMod31M_K_;
  393.           END;
  394.  
  395.         LoadMod(Song, St, Mod31);
  396.       END
  397.     ELSE IF (Mod31.Magic = Mod31MagicFLT4) THEN
  398.       BEGIN
  399.         Song.NumChannels := 4;
  400.         Song.FileFormat  := mffMod31FLT4;
  401.         LoadMod(Song, St, Mod31);
  402.       END
  403.     ELSE IF (Mod31.Magic = Mod31Magic6CHN) THEN
  404.       BEGIN
  405.         Song.NumChannels := 6;
  406.         Song.FileFormat  := mffFastTracker;
  407.         LoadMod(Song, St, Mod31);
  408.       END
  409.     ELSE IF (Mod31.Magic = Mod31Magic8CHN) THEN
  410.       BEGIN
  411.         Song.NumChannels := 8;
  412.         Song.FileFormat  := mffFastTracker;
  413.         LoadMod(Song, St, Mod31);
  414.       END
  415.     ELSE
  416.       BEGIN
  417.         Song.NumChannels := 4;
  418.         Song.FileFormat  := mffMod15;
  419.         LoadMod15(Song, St, Header);
  420.       END
  421.  
  422.   END;
  423.  
  424.  
  425.  
  426.  
  427. END.
  428.